
系列:30 天打造企業級 PLM|面向:全端
簽核的人最想知道的只有一件事:這次到底改了什麼?把改前改後兩份完整資料丟給他自己比對,是簽核體驗的謀殺。Redline(紅線標記,源自工程圖紙上用紅筆標改動的概念)要做的就是欄位級、BOM 行級的差異,修改的內容標記出來。今天講差異怎麼存、怎麼呈現。
先把話說在前面:Redline 是 Oracle Agile PLM 的招牌賣點,不是它的弱項。ECO 上的 Redline BOM / Redline AML 是整個變更管理的核心體驗,SDK 也給得很完整——透過 IChange 取得變更單、用 ITable 開 ChangeConstants.TABLE_REDLINEBOM 之類的 redline 表,就能逐行讀出這張單相對於原始 BOM 的增刪改;報表面也有現成的變更報表與 BOM 比對輸出。做過 Agile 客製的人都知道,這塊的成熟度是十幾年累積出來的。
所以自建 PLM 在這一章的題目不是「怎麼贏過 Agile」,而是怎麼把這套成熟的變更差異機制,用開放的資料結構重新長出來。差別在哪?Agile 的 redline 是綁在它自己的 schema、授權模型與 UI 上的:差異資料的形狀由產品定義,你能取用,但取用的路徑得走它的 SDK 與物件模型;要把差異送進自家的簽核路由、通知、看板,中間永遠隔著一層轉譯。自建的價值就在把這層轉譯拿掉——差異從第一天就以自己的 JSON 結構落地,誰要用都直接讀。
Mini-PLM 採用增量為主、快照為輔的結構化 JSON Redline 實體設計:
/** 動作類型(ADD/UPDATE/REPLACE/REMOVE) */
@Enumerated(EnumType.STRING)
private BomRedlineActionEnum actionType;
/** 目標 BOM 行 ID(UPDATE/REPLACE/REMOVE 必填) */
private Long targetBiId;
/** Change Out(JSON 快照) */
@Type(type = "com.miniplm.config.ClobStringType")
private String changeOutJson;
/** Change In(JSON 快照) */
private String changeInJson;
/** PATCH(JSON;UPDATE/REPLACE 用) */
private String patchJson;
一列 redline 等於一個動作(ADD / UPDATE / REPLACE / REMOVE)加三份 JSON:changeOutJson 是被改掉的那行當時長什麼樣(快照),changeInJson 是改完之後長什麼樣(快照),patchJson 是實際動到的欄位(增量)。
為什麼三份都要?各自服務不同讀者。patch 給差異視覺化,只標動過的欄位;changeOut 可看到何時移出,就算原始 BOM 行後來被別的變更改了,還是能看出當時的樣子;changeIn 紀錄何時加入的。
補一個型別細節:三個 JSON 欄位用自訂 ClobStringType 對映,這是跨 Oracle(CLOB)與 PostgreSQL(text)的大文字欄位處理,Day 27 的伏筆。
ADD / UPDATE / REMOVE 好懂。REPLACE(換料:A 料換成 B 料,位置與用量不變)看似 UPDATE 的特例,卻獨立成動作,因為業務語意不同:UPDATE 是同一顆料改屬性,REPLACE 是換供應、換件,下游的採購通知、庫存處置完全兩回事。實體上的註解也留了規則:「Find Number:ADD 必填;REPLACE 必須與原行相同」,換料不換位置。diff 的動作分類要按業務語意設計,不是按資料庫操作設計。資料庫視角 REPLACE 就是 UPDATE,業務視角天差地遠。

上圖是實機畫面:同一張變更單的 BOM 上,UPDATE 只把改動的用量標成橘色、REPLACE 上下兩列並排(舊料刪除線、新料綠色,Seq. No 不變)、REMOVE 整列紅色刪除線、沒動到的那列標 No Change 淡出當背景、ADD 整列綠色。合併預覽是 base BOM 疊上 redline actions 算出來的,每一列都能單獨 Undo。
Item 欄位的 redline 同樣以 pending_data(Day 13)為基準:pending 與 original 逐欄比對,前端呈現三態,新增綠色、修改原值刪除線加新值、移除紅色。實作上有兩條規矩。只標有效差異:null 對空字串、多餘空白這類假差異要在比對前正規化掉,否則簽核者滿眼紅線全是雜訊。多選欄位比集合不比字串:multilist 存 JSON 陣列(Day 4),["A","B"] 與 ["B","A"] 是同一個值,字串比對會誤報。
核可供應商清單(AML)的變更走同一套 redline 架構(FormItemAmlRedline,鏡像 BOM redline 的欄位設計),動作分類、三份 JSON、放行套用全部同款。第二次實作同一個模式時,就值得把模式固定下來:之後任何行級資料的受控變更,例如未來的替代料清單,都有現成骨架。
saveAll 批次化後降一個數量級。Day 21 壓測抓出的另一個熱點,跟 Day 14 的 INSERT-SELECT 是同一堂課:ORM 逐筆操作不適合批次場景changeOutJson 快照存在的理由就在這。Mini-PLM 早期版本沒存快照,兩張並行變更單會互相污染對方的差異顯示——這正是 Agile 用 pending revision 鎖定(Day 13)擋掉的問題,換一套架構就得自己補上等價的保護復現 Agile 的招牌功能,關鍵不在畫面像不像,而在差異資料的結構自己說得清楚:動作加三份 JSON,增量給呈現、快照給稽核與套用;動作分類按業務語意,REPLACE 不等於 UPDATE;假差異在比對前先正規化。差異看得懂之後,下一個問題是怎麼找到我要的東西。明日 Day 16:進階搜尋,條件建構器與幾百個動態欄位的動態查詢。